package pixel;

/*
 * verstecken des Textes aus der Datei "klar.txt" in dem Bild "BILD.png"
 * Diese muss im Format ARGB vorliegen, was aber nicht geprüft wird
 * und was auch NICHT üBLICH ist!
 * Deshalb wird ohne Ansehen des ColorModel das LSB
 * eines RGB-Werts aus/in einem PNG-Bild benutzt
 * also:
 * ein Bit pro Pixel
 *
 * Es wird das LSB des Pixels
 *   auf das nächste Bit der Zeichen des Klartexts gesetzt (von MSB nach LSB)
 *   Vor den Zeichen des Klartexts werden nur die niedrigsten 8 Bits benutzt.
 * Wir brauchen also 8 Pixel pro Zeichen Klartext. (Dieser wird notfalls gekürzt.)
 * Das Ergebnis-Bild wird unter stegano.png gespeichert.
 */

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
import java.io.*;

public class Stegano extends JApplet {
  String inFileName="monalisa.png";
  String outFileName="stegano.png";
  String klarTextFileName="klartext.txt";
  private JButton btnStart;
  private JButton btnVerstecken;
  private JButton btnSteganoSpeichern;
  private SteganoPanel pZentral;

  private void erzeugeKomponenten(){
    btnStart=new JButton("Original laden ("+inFileName+")");
    btnVerstecken=new JButton("Text verstecken");
    btnSteganoSpeichern=new JButton("speichern ("+outFileName+")");
    pZentral=new SteganoPanel();
  }

  private void registriereZuhoerer(){
    btnStart.addActionListener(new BL());
    btnVerstecken.addActionListener(new BLFaerben());
    btnSteganoSpeichern.addActionListener(new BLSteganoSpeichern());
  }

  private void baueLayout(){
    Container cp=getContentPane();
    cp.setLayout(new BorderLayout());
    // NORTH
    JPanel pN=new JPanel(new FlowLayout(FlowLayout.RIGHT));
    pN.add(btnSteganoSpeichern);
    pN.add(btnVerstecken);
    pN.add(btnStart);
    cp.add(pN, BorderLayout.NORTH);
    // CENTER
    cp.add(pZentral, BorderLayout.CENTER);
  }

  @Override
  public void init(){
    erzeugeKomponenten();
    registriereZuhoerer();
    baueLayout();
    try{
      pZentral.dasPNGBild=ImageIO.read(new File(inFileName));
      pZentral.dasSteganoBild=ImageIO.read(new File(inFileName));
    } catch(IOException ioe){System.out.println(ioe);}
    pZentral.istKnopfGedrueckt=false;
  }

  public class BL implements ActionListener{
    @Override
    public void actionPerformed(ActionEvent e){
      pZentral.istKnopfGedrueckt=true;
      pZentral.repaint();
    }
  }

  public class BLFaerben implements ActionListener{
    @Override
    public void actionPerformed(ActionEvent e){
      pZentral.istKnopfGedrueckt=true;
      pZentral.istVerstecken=true;
      versteckeText();
      pZentral.repaint();
    }
  }

  public class BLSteganoSpeichern implements ActionListener{
    @Override
    public void actionPerformed(ActionEvent e){
      speichereSteganoBild();
    }
  }

  private int setzeBit(int inInteger, int anPos) {
    return inInteger | (1 << anPos);
  }

  private int loescheBit(int inInteger, int anPos) {
    return ~((~inInteger) | (1 << anPos));
  }

  private boolean istBitgesetzt(int inInteger, int anPos) {
    return ((inInteger >>> anPos) & 1) == 1;
  }

 /* verstecke Text (aus der Datei klar.txt)
  *  bitweise in dem Bild
  *  jeweils das unterste Bit (LSBit) wird auf den Wert
  *  des Textzeichens gesetzt.
  *  Dabei werden die Bits in der Reihenfolge von MSB nach LSB
  *  genommen, jeweils 8 pro Klartext-Zeichen.
  */
  private void versteckeText(){
    BufferedReader inFile=null;
    String zeile="";
    String klartext="";
    boolean istFertig=false;
    int buchstabenPos=0;
    int bitInBuchstabe=7; //links anfangen
    int rgbWert;
    int breite=pZentral.dasSteganoBild.getWidth();
    int hoehe =pZentral.dasSteganoBild.getHeight();
    System.out.println("Stegano-Bild:");
    System.out.println("Breite: "+breite+"   Höhe: "+hoehe);
    // Klartext.txt in klarttext einlesen; Nach jeder zeile ein Leerzeichen
    try{
      inFile=new BufferedReader(new FileReader(klarTextFileName));
      while((zeile=inFile.readLine())!=null)
        klartext=klartext+zeile+" ";
      inFile.close();
    } catch(IOException ioe){
      System.out.println("Fehler beim Lesen von "+klarTextFileName+": "+ioe);
      System.exit(2);
    }
    //System.out.println("klartext: >>"+klartext+"<<");
    int laenge=klartext.length();
    //System.out.println("Laenge von klartext: "+laenge);
    //8 Pixel werden pro klartext-Buchstaben benoetigt:
    int maxLaenge=breite*hoehe/8;
    if(laenge>maxLaenge){
      klartext=klartext.substring(0, maxLaenge);
      laenge=klartext.length();
      System.out.println("klartext zu lang!");
      System.out.println("klartext wurde auf Laenge "+laenge+" gekuerzt");
    }
    istFertig=laenge<1;
    for(int y=0; y<hoehe; y++){
      for(int x=0; x<breite; x++){
        if(!istFertig){
          rgbWert=pZentral.dasSteganoBild.getRGB(x, y);
          //System.out.println("RGB:"+rgbWert);
          if(istBitgesetzt(klartext.charAt(buchstabenPos), bitInBuchstabe))
            rgbWert=setzeBit(rgbWert, 0);
          else rgbWert=loescheBit(rgbWert, 0);
          bitInBuchstabe--;
          if(bitInBuchstabe<0){// naechster Buchstabe
            bitInBuchstabe=7;
            buchstabenPos++;
          }
          istFertig=buchstabenPos>=laenge;
          pZentral.dasSteganoBild.setRGB(x, y, rgbWert);
          //System.out.println("RGB:"+rgbWert);
        }
      }
    }
  }

  private void speichereSteganoBild(){
    try{
      ImageIO.write(pZentral.dasSteganoBild, "PNG", new File(outFileName));
    } catch(IOException ioe){
      System.err.println("Fehler beim Schreiben von: "+outFileName+": "+ioe);
      System.exit(2);
    }
  }

  public static void main(String[] args){
    Stegano meinAppchen=new Stegano();
    JFrame meinFrame=new JFrame("Stegano");
    meinFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    meinFrame.getContentPane().add(meinAppchen, BorderLayout.CENTER);
    meinAppchen.init();
    meinAppchen.start();
    meinFrame.setSize(600, 500);
    meinFrame.setVisible(true);
  }
}
class SteganoPanel extends JPanel{
  boolean istKnopfGedrueckt=false;
  boolean istVerstecken=false;
  BufferedImage dasPNGBild=null;
  BufferedImage dasSteganoBild=null;

  @Override
  public void paintComponent(Graphics g){
    super.paintComponent(g);
    if (istKnopfGedrueckt){
      g.drawImage(dasPNGBild, 10, 100, this);
      if(istVerstecken){
        g.drawImage(dasSteganoBild, 250, 100, this);
        istVerstecken=false;// nur einmal
      }
    }
  }
}
